unit uRegMonitorThread;

interface

uses
    Classes, Windows, Messages, Registry, SysUtils;

Const
  WM_REGCHANGE = WM_USER + 1973;


function RegNotifyChangeKeyValue(hKey: HKEY; bWatchSubtree: DWORD; dwNotifyFilter: DWORD; hEvent: THandle; fAsynchronus: DWORD): Longint; stdcall;
            external 'advapi32.dll' name 'RegNotifyChangeKeyValue';

Type TChangeData = record
    Key : string;
    Rootkey : HKey;
    end;




type
  TRegMonitorThread = class(TThread)
  private
    { Private declarations }
    FReg        :       TRegistry;
    FEvent      :       Integer;
    FChangeData :       TChangeData;
    FKey        :       string;
    FRootKey    :       HKey;
    FWatchSub   :       boolean;
    FFilter     :       integer;
    FWnd        :       THandle;

    PROCEDURE InitThread();

    PROCEDURE SetFilter(const Value: Integer);
    FUNCTION GetFilter() : integer;
    FUNCTION GetChangeData() : TChangeData;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;

    property Key : String read FKey write FKey;
    property WatchSub : Boolean read FWatchSub write FWatchSub;
    property Filter : integer read GetFilter write SetFilter;
    property Wnd : THandle read FWnd write FWnd;
    property ChangeData : TChangeData read GetChangeData;
    property RootKey : HKEY read FRootKey write FRootKey;

  end;

implementation

{
  Important: Methods and properties of objects in visual components can only be
  used in a method called using Synchronize, for example,

      Synchronize(UpdateCaption);

  and UpdateCaption could look like,

    procedure TRegMon.UpdateCaption;
    begin
      Form1.Caption := 'Updated in a thread';
    end;

    or

    Synchronize(
      procedure
      begin
        Form1.Caption := 'Updated in thread via an anonymous method' 
      end
      )
    );
    
  where an anonymous method is passed.
  
  Similarly, the developer can call the Queue method with similar parameters as 
  above, instead passing another TThread class as the first parameter, putting
  the calling thread in a queue with the other thread.
    
}

{ TRegMon }

CONSTRUCTOR TRegMonitorThread.Create;
begin
  Inherited Create(True);

  FReg := TRegistry.Create;
end;

DESTRUCTOR TRegMonitorThread.Destroy;
begin
  FReg.Free;

  Inherited Destroy;
end;

PROCEDURE TRegMonitorThread.InitThread;
begin
  FReg.RootKey := RootKey;

  if not FReg.OpenKeyReadOnly(Key) then
      Begin
      raise Exception.Create('Unable to open registry key ' + Key);
      End;

  FEvent := CreateEvent(nil, True, False, 'RegMonitorChange');
  RegNotifyChangeKeyValue(FReg.CurrentKey,1,Filter,FEvent,1);
end;

procedure TRegMonitorThread.Execute;
begin
  { Place thread code here }

  InitThread;

  while not Terminated do
     Begin
       if WaitForSingleObject(FEvent, INFINITE) = WAIT_OBJECT_0 then
          Begin
            FChangeData.Rootkey := FRootKey;
            FCHangeData.Key := FKey;
            SendMessage(Wnd, WM_REGCHANGE, RootKey,  LongInt(PChar(Key)));
            ResetEvent(FEvent);
            RegNotifyChangeKeyValue(FReg.CurrentKey,1,Filter,FEvent,1);
          End;
     End;
end;

PROCEDURE TRegMonitorThread.SetFilter(const Value: Integer);
begin
  if FFilter <> Value then
     Begin
       FFilter := Value;
     End;
end;

FUNCTION TRegMonitorThread.GetFilter() : integer;
begin
if FFilter = 0 then
   Begin
     FFilter :=
        REG_NOTIFY_CHANGE_NAME or
        REG_NOTIFY_CHANGE_aTTRIBUTES or
        REG_NOTIFY_CHANGE_LAST_SET or
        REG_NOTIFY_CHANGE_sECURITY;
   End;
Result := FFilter;
end;

FUNCTION TRegMonitorThread.GetChangeData() : TChangeData;
begin
Result := FCHangeData;
end;


end.
